using System;
using System.Collections.Generic;
using MetaObjects.Contracts;
using System.Linq;

namespace MetaObjects.Core
{
	public class MapperProvider
	{
		public IMetaEntityProvider SourceProvider { get; private set; }
		public IMetaEntityProvider TargetProvider { get; private set; }

		public MapperProvider(IMetaEntityProvider source, IMetaEntityProvider target)
		{
			SourceProvider = source;
			TargetProvider = target;
		}

		public IMapper<TSource, TTarget> CreateMapper<TSource, TTarget>(object source, object target)
		{
			IMetaEntity sourceMetaEntity = SourceProvider.EntityFactory(source);
			IMetaEntity targetMetaEntity = TargetProvider.EntityFactory(target);
			return new ActionMapper<TSource, TTarget>(() => (TTarget)targetMetaEntity.Factory(), GetActions<TSource, TTarget>(sourceMetaEntity, targetMetaEntity).ToList());
		}

		//public IMapper<TSource, TTarget> CreateMapper2<TSource, TTarget>(object source, object target)
		//{
		//    IMetaEntity sourceMetaEntity = SourceProvider.EntityFactory(source);
		//    IMetaEntity targetMetaEntity = TargetProvider.EntityFactory(target);
		//    return new EntityMapper<TSource, TTarget>(CreateMapFunc<TSource, TTarget>(sourceMetaEntity, targetMetaEntity));
		//}

		//public IMapper<TSource, TTarget> CreateMapper3<TSource, TTarget>(object source, object target)
		//{
		//    IMetaEntity sourceMetaEntity = SourceProvider.EntityFactory(source);
		//    IMetaEntity targetMetaEntity = TargetProvider.EntityFactory(target);
		//    return new ActionMapper<TSource, TTarget>(() => (TTarget)targetMetaEntity.Factory(), GetActions<TSource, TTarget>(sourceMetaEntity, targetMetaEntity).ToList());
		//}

		//protected virtual TTarget MapInternal<TSource, TTarget>(TSource source)
		//{
		//    IMetaEntity sourceMetaEntity = SourceProvider.EntityFactory(source);
		//    IMetaEntity targetMetaEntity = TargetProvider.EntityFactory(target);

		//    TTarget target = (TTarget)targetMetaEntity.Factory();
		//    foreach (var metaField in sourceMetaEntity.MetaFields)
		//    {
		//        var name = metaField.InternalName;
		//        if (targetMetaEntity.MetaFields.Contains(name))
		//        {
		//            var targetField = targetMetaEntity.MetaFields[name];
		//            IMetaField sourceField = metaField;
		//            targetField.SetField(target, sourceField.GetField(source));
		//        }
		//    }
		//    return target;
		//}

		//private static Func<TSource, TTarget> CreateMapFunc<TSource, TTarget>(IMetaEntity sourceMetaEntity, IMetaEntity targetMetaEntity)
		//{
		//    Func<TSource, TTarget> map = s => (TTarget)targetMetaEntity.Factory();
		//    foreach (var metaField in sourceMetaEntity.MetaFields)
		//    {
		//        var name = metaField.InternalName;
		//        if (targetMetaEntity.MetaFields.Contains(name))
		//        {
		//            var targetField = targetMetaEntity.MetaFields[name];
		//            IMetaField sourceField = metaField;

		//            Func<TSource, TTarget> func = map;
		//            map = s => MapperExtensions.ApplyAction(s, func(s), (src, tgt) => targetField.SetField(tgt, sourceField.GetField(src)));
		//        }
		//    }
		//    return map;
		//}

		private static IEnumerable<Action<TSource, TTarget>> GetActions<TSource, TTarget>(IMetaEntity sourceMetaEntity, IMetaEntity targetMetaEntity)
		{
			foreach (var metaField in sourceMetaEntity.MetaFields)
			{
				var name = metaField.InternalName;
				if (targetMetaEntity.MetaFields.Contains(name))
				{
					var targetField = targetMetaEntity.MetaFields[name];
					IMetaField sourceField = metaField;
					Action<TSource,TTarget> action = (s,t) => targetField.SetField(t, sourceField.GetField(s));
					yield return action;
				}
			}
		}
	}
}